/*
 * (C) Copyright 2015 by fr3ts0n <erwin.scheuch-heilig@gmx.at>
 *
 * This program is free software; you can redistribute it and/or
 * modify it under the terms of the GNU General Public License as
 * published by the Free Software Foundation; either version 2 of
 * the License, or (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 59 Temple Place, Suite 330, Boston,
 * MA 02111-1307 USA
 */

package com.fr3ts0n.ecu.gui.application;

import com.fr3ts0n.ecu.EcuCodeItem;
import com.fr3ts0n.ecu.ObdCodeItem;
import com.fr3ts0n.pvs.PvChangeEvent;
import com.fr3ts0n.pvs.PvChangeListener;
import com.fr3ts0n.pvs.PvList;

import java.awt.Color;
import java.awt.event.ActionListener;
import java.beans.PropertyChangeListener;

import javax.swing.ImageIcon;
import javax.swing.JOptionPane;
import javax.swing.JPanel;
import javax.swing.ListSelectionModel;
import javax.swing.event.ListSelectionEvent;
import javax.swing.event.ListSelectionListener;


/**
 * GUI Panel to control reading/clearing OBD failure codes
 *
 * @author erwin
 */
public class ObdDtcPanel extends JPanel
	implements PropertyChangeListener, PvChangeListener
{
	/** serial version UID  * */
	private static final long serialVersionUID = -1285434908785275242L;
	/** icons */
	private final ImageIcon icoMilOff = new javax.swing.ImageIcon(getClass().getResource("/com/fr3ts0n/ecu/gui/res/mil_off.png"));
	private final ImageIcon icoMilOn = new javax.swing.ImageIcon(getClass().getResource("/com/fr3ts0n/ecu/gui/res/mil_on.png"));

	/** milStatus */
	private boolean milStatus = false;

	/** Creates new form ObdDfcPanel */
	public ObdDtcPanel()
	{
		initComponents();
		panStatus.setVisible(false);
		tblFCodes.setSelectionMode(ListSelectionModel.SINGLE_SELECTION);
		tblFCodes.getSelectionModel().addListSelectionListener(new ListSelectionListener()
		{
			public void valueChanged(ListSelectionEvent e)
			{
				Integer stat = null;
				int selIdx = tblFCodes.getSelectedRow();
				if (selIdx >= 0)
					stat = (Integer) tblFCodes.getModel().getValueAt(selIdx, EcuCodeItem.FID_STATUS);
				panStatus.setVisible(stat != null);
				if (stat != null)
				{
					panStatus.setStatusFlags(stat.intValue());
				}
			}
		});
	}

	/**
	 * This method is called from within the constructor to
	 * initialize the form.
	 * WARNING: Do NOT modify this code. The content of this method is
	 * always regenerated by the Form Editor.
	 */
	// <editor-fold defaultstate="collapsed" desc="Generated Code">//GEN-BEGIN:initComponents
	private void initComponents()
	{
		java.awt.GridBagConstraints gridBagConstraints;
		
		JPanel panHeader = new JPanel();
		lblCodeType = new javax.swing.JLabel();
		lblMil = new javax.swing.JLabel();
		lblMil1 = new javax.swing.JLabel();
		lblNumCodes = new javax.swing.JLabel();
		JPanel panFooter = new JPanel();
		btnReadCodes = new javax.swing.JButton();
		btnReadPending = new javax.swing.JButton();
		btnReadPermanent = new javax.swing.JButton();
		btnClearCodes = new javax.swing.JButton();
		JPanel panCenter = new JPanel();
		javax.swing.JScrollPane jScrollPane1 = new javax.swing.JScrollPane();
		tblFCodes = new com.fr3ts0n.pvs.gui.PvTable();
		panStatus = new com.fr3ts0n.ecu.gui.application.VagCodeStatPanel();

		setLayout(new java.awt.BorderLayout());

		panHeader.setBackground(new java.awt.Color(255, 255, 255));
		panHeader.setLayout(new java.awt.BorderLayout(10, 10));

		lblCodeType.setHorizontalAlignment(javax.swing.SwingConstants.CENTER);
		lblCodeType.setText("Trouble codes");
		panHeader.add(lblCodeType, java.awt.BorderLayout.CENTER);

		lblMil.setHorizontalAlignment(javax.swing.SwingConstants.CENTER);
		lblMil.setIcon(icoMilOff);
		lblMil.setHorizontalTextPosition(javax.swing.SwingConstants.RIGHT);
		panHeader.add(lblMil, java.awt.BorderLayout.WEST);

		lblMil1.setHorizontalAlignment(javax.swing.SwingConstants.CENTER);
		lblMil1.setIcon(icoMilOff);
		lblMil1.setHorizontalTextPosition(javax.swing.SwingConstants.LEFT);
		panHeader.add(lblMil1, java.awt.BorderLayout.EAST);

		lblNumCodes.setFont(new java.awt.Font("Dialog", 0, 10));
		lblNumCodes.setHorizontalAlignment(javax.swing.SwingConstants.CENTER);
		lblNumCodes.setText("0 Trouble codes set");
		panHeader.add(lblNumCodes, java.awt.BorderLayout.SOUTH);

		add(panHeader, java.awt.BorderLayout.NORTH);

		panFooter.setLayout(new java.awt.GridBagLayout());

		btnReadCodes.setFont(new java.awt.Font("Dialog", 0, 10));
		btnReadCodes.setMnemonic('R');
		btnReadCodes.setText("Read Codes");
		btnReadCodes.setToolTipText("Read all truble codes (Mode $3)");
		btnReadCodes.setActionCommand("ReadCodes");
		btnReadCodes.addActionListener(new java.awt.event.ActionListener()
		{
			public void actionPerformed(java.awt.event.ActionEvent evt)
			{
				btnReadCodesActionPerformed();
			}
		});
		gridBagConstraints = new java.awt.GridBagConstraints();
		gridBagConstraints.fill = java.awt.GridBagConstraints.BOTH;
		gridBagConstraints.weightx = 1.0;
		panFooter.add(btnReadCodes, gridBagConstraints);

		btnReadPending.setFont(new java.awt.Font("Dialog", 0, 10));
		btnReadPending.setMnemonic('P');
		btnReadPending.setText("Read Pending");
		btnReadPending.setToolTipText("Read pending fault codes (Mode $7)");
		btnReadPending.setActionCommand("ReadPending");
		btnReadPending.addActionListener(new java.awt.event.ActionListener()
		{
			public void actionPerformed(java.awt.event.ActionEvent evt)
			{
				btnReadPendingActionPerformed();
			}
		});
		gridBagConstraints = new java.awt.GridBagConstraints();
		gridBagConstraints.fill = java.awt.GridBagConstraints.BOTH;
		gridBagConstraints.weightx = 1.0;
		panFooter.add(btnReadPending, gridBagConstraints);

		btnReadPermanent.setFont(new java.awt.Font("Dialog", 0, 10));
		btnReadPermanent.setMnemonic('e');
		btnReadPermanent.setText("Read Permanent");
		btnReadPermanent.setToolTipText("Read permanent Trouble codes (Mode $A)");
		btnReadPermanent.setActionCommand("ReadPermanent");
		btnReadPermanent.addActionListener(new java.awt.event.ActionListener()
		{
			public void actionPerformed(java.awt.event.ActionEvent evt)
			{
				btnReadPermanentActionPerformed();
			}
		});
		gridBagConstraints = new java.awt.GridBagConstraints();
		gridBagConstraints.fill = java.awt.GridBagConstraints.BOTH;
		gridBagConstraints.weightx = 1.0;
		panFooter.add(btnReadPermanent, gridBagConstraints);

		btnClearCodes.setFont(new java.awt.Font("Dialog", 0, 10));
		btnClearCodes.setMnemonic('C');
		btnClearCodes.setText("Clear Codes");
		btnClearCodes.setToolTipText("Clear troble codes (Mode $4)");
		btnClearCodes.setActionCommand("ClearCodes");
		btnClearCodes.addActionListener(new java.awt.event.ActionListener()
		{
			public void actionPerformed(java.awt.event.ActionEvent evt)
			{
				btnClearCodesActionPerformed(evt);
			}
		});
		gridBagConstraints = new java.awt.GridBagConstraints();
		gridBagConstraints.fill = java.awt.GridBagConstraints.BOTH;
		gridBagConstraints.weightx = 1.0;
		panFooter.add(btnClearCodes, gridBagConstraints);

		add(panFooter, java.awt.BorderLayout.SOUTH);

		panCenter.setLayout(new java.awt.BorderLayout());

		tblFCodes.setAutoResizeMode(5);
		tblFCodes.setName("CodeTable"); // NOI18N
		jScrollPane1.setViewportView(tblFCodes);

		panCenter.add(jScrollPane1, java.awt.BorderLayout.CENTER);
		panCenter.add(panStatus, java.awt.BorderLayout.SOUTH);

		add(panCenter, java.awt.BorderLayout.CENTER);
	}// </editor-fold>//GEN-END:initComponents

	private void btnReadPendingActionPerformed()//GEN-FIRST:event_btnReadPendingActionPerformed
	{//GEN-HEADEREND:event_btnReadPendingActionPerformed
		lblCodeType.setText("Pending trouble codes");
	}//GEN-LAST:event_btnReadPendingActionPerformed

	private void btnClearCodesActionPerformed(java.awt.event.ActionEvent evt)//GEN-FIRST:event_btnClearCodesActionPerformed
	{//GEN-HEADEREND:event_btnClearCodesActionPerformed
		if (JOptionPane.showConfirmDialog(this,
			"This will reset the MIL and clear all emission-related diagnostic\n"
				+ " information, including:\n\n"
				+ " - Diagnostic trouble codes\n"
				+ " - Freeze frame data\n"
				+ " - Oxygen sensor com.fr3ts0n.test data\n"
				+ " - Status of system monitoring tests\n"
				+ " - On-board monitoring tests results\n"
				+ " - Distance travelled while MIL activated\n"
				+ " - Number of warm-ups since DTCs cleared\n"
				+ " - Distance travelled since DTCs cleared\n"
				+ " - Engine run time while MIL activated\n"
				+ " - Time since DTCs cleared\n\n"
				+ "Other manufacturer-specific 'clearing/resetting' actions may occur.\n"
				+ "The loss of data may cause the vehicle to run poorly for a short \n"
				+ "period of time while the ECU recalibrates itself.",
			"Clear codes?",
			JOptionPane.YES_NO_OPTION,
			JOptionPane.WARNING_MESSAGE) != JOptionPane.YES_OPTION)
			evt.setSource(null);
	}//GEN-LAST:event_btnClearCodesActionPerformed

	private void btnReadCodesActionPerformed()//GEN-FIRST:event_btnReadCodesActionPerformed
	{//GEN-HEADEREND:event_btnReadCodesActionPerformed
		lblCodeType.setText("Stored trouble codes");
	}//GEN-LAST:event_btnReadCodesActionPerformed

	private void btnReadPermanentActionPerformed()
	{//GEN-FIRST:event_btnReadPermanentActionPerformed
		lblCodeType.setText("Permanent trouble codes");
	}//GEN-LAST:event_btnReadPermanentActionPerformed


	// Variables declaration - do not modify//GEN-BEGIN:variables
	private javax.swing.JButton btnClearCodes;
	private javax.swing.JButton btnReadCodes;
	javax.swing.JButton btnReadPending;
	javax.swing.JButton btnReadPermanent;
	private javax.swing.JLabel lblCodeType;
	private javax.swing.JLabel lblMil;
	private javax.swing.JLabel lblMil1;
	private javax.swing.JLabel lblNumCodes;
	private com.fr3ts0n.ecu.gui.application.VagCodeStatPanel panStatus;
	private com.fr3ts0n.pvs.gui.PvTable tblFCodes;
	// End of variables declaration//GEN-END:variables

	public boolean getMilStatus()
	{
		return (milStatus);
	}

	private void setMilStatus(boolean newStatus)
	{
		milStatus = newStatus;
		// set the MIL-status display
		String msg = milStatus ? "MIL is ON" : "MIL is OFF";
		ImageIcon icn = milStatus ? icoMilOn : icoMilOff;
		Color clr = milStatus ? Color.RED : Color.GREEN;
		lblMil.setIcon(icn);
		lblMil.setText(msg);
		lblMil.setToolTipText(msg);
		lblMil.setForeground(clr);
		lblMil1.setIcon(icn);
		lblMil1.setText(msg);
		lblMil1.setToolTipText(msg);
		lblMil1.setForeground(clr);
	}

	/**
	 * Holds value of property tcList.
	 */
	private PvList tcList;

	/**
	 * Holds value of property numCodes.
	 */
	private int numCodes;

	/**
	 * Getter for property pvList.
	 *
	 * @return Value of property pvList.
	 */
	public PvList getTcList()
	{
		return this.tcList;
	}

	/**
	 * Setter for trouble code list
	 *
	 * @param tcList New value of trouble code list.
	 */
	public void setTcList(PvList tcList)
	{
		this.tcList = tcList;
		// set the process var list of the table
		tblFCodes.setProcessVar(tcList);
		tcList.addPvChangeListener(this);
		updateColumnWidths();
	}

	/**
	 * Getter for property numCodes.
	 *
	 * @return Value of property numCodes.
	 */
	public int getNumCodes()
	{
		return this.numCodes;
	}

	/**
	 * Setter for property numCodes.
	 *
	 * @param numCodes New value of property numCodes.
	 */
	private void setNumCodes(int numCodes)
	{
		this.numCodes = numCodes & 0x7F;
		setMilStatus((numCodes & 0x80) != 0);
		lblNumCodes.setText(this.numCodes + " Trouble codes set");
	}

	/**
	 * update the column widths of data table
	 */
	private void updateColumnWidths()
	{
		if (tcList.size() > 0)
		{
			/** set column sizes here, since this only works with inserted data */
			tblFCodes.getColumn(ObdCodeItem.FIELDS[ObdCodeItem.FID_CODE]).setPreferredWidth(40);
			tblFCodes.getColumn(ObdCodeItem.FIELDS[ObdCodeItem.FID_DESCRIPT]).setPreferredWidth(330);
		}
	}

	/**
	 * This method gets called when a bound property is changed.
	 *
	 * @param evt A PropertyChangeEvent object describing the event source
	 *            and the property that has changed.
	 */
	public void propertyChange(java.beans.PropertyChangeEvent evt)
	{
		if (evt.getPropertyName().equals("numCodes"))
		{
			setNumCodes(((Integer) evt.getNewValue()).intValue());
		}
	}

	/**
	 * special handling of changes in displayed code list
	 *
	 * @param event Event to be handled
	 */
	public void pvChanged(PvChangeEvent event)
	{
		switch (event.getType())
		{
	  /* update column width on added lines, since this only works with
       * tables containing at least one row of data
       */
			case PvChangeEvent.PV_ADDED:
				updateColumnWidths();
				break;
		}
	}

	/**
	 * Add actionListener to all action sources on panel
	 *
	 * @param al ActionListener to be registered
	 */
	public void addActionListener(ActionListener al)
	{
		btnClearCodes.addActionListener(al);
		btnReadCodes.addActionListener(al);
		btnReadPending.addActionListener(al);
		btnReadPermanent.addActionListener(al);
	}

	/**
	 * Remove actionListener from all action sources on panel
	 *
	 * @param al ActionListener to be registered
	 */
	public void removeActionListener(ActionListener al)
	{
		btnClearCodes.removeActionListener(al);
		btnReadCodes.removeActionListener(al);
		btnReadPending.removeActionListener(al);
		btnReadPermanent.removeActionListener(al);
	}
}
